#include <fuquOS/io.h>
#include <fuquOS/number.h>
#include <fuquOS/pit.h>
#include <fuquOS/printk.h>
#include <fuquOS/stdarg.h>
#include <fuquOS/stdio.h>
#include <fuquOS/string.h>

int x = 0, y = 0;

void clear() {
    u16 *video_memory = (u16 *) 0xb8000;
    for (u32 i = 0; i < 80 * 50; i++) {
        putc(' ');
    }
    x = 0, y = 0;
    set_cursor(0, 0);
}

void putc(char c) {
    u16 *vga = (u16 *) 0xb8000;
    if (c == '\n') {
        y++;
        x = 0;
    } else if (c == '\t') {
        x = (x + 8) & -7;
    } else {
        u16 value = (0x07 << 8) | c;
        vga[y * 80 + x] = value;
        x++;
        if (x >= 80) {
            x -= 80;
            y++;
        }
    }

    if (y >= 25) {
        scroll();
    }
    set_cursor(y, x);
}

void putc_color(char c, char color) {
    u16 *vga = (u16 *) 0xb8000;
    if (c == '\n') {
        y++;
        x = 0;
    } else if (c == '\t') {
        x = (x + 8) & -7;
    } else {
        u16 value = (color << 8) | c;
        vga[y * 80 + x] = value;
        x++;
        if (x >= 80) {
            x -= 80;
            y++;
        }
    }
    if (y > 25) {
        scroll();
    }
    set_cursor(y, x);
}

void puts(const char *s) {
    for (int i = 0; s[i] != '\0'; i++) {
        putc(s[i]);
    }
}

void puts_color(const char *s, char color) {
    for (int i = 0; s[i] != '\0'; i++) {
        putc_color(s[i], color);
    }
}

void set_cursor(int row, int col) {
    unsigned short position = (row * 80) + col; // 计算光标偏移位置

    // 写入高字节
    outb(0x3d4, 0xe);
    outb(0x3d5, (unsigned char) (position >> 8));

    // 写入低字节
    outb(0x3d4, 0xf);
    outb(0x3d5, (unsigned char) (position & 0xff));

    if (col >= 80) {
        col -= 80;
        row++;
    }
}

void scroll() {
    u16 *vga = (u16 *) 0xb8000;
    memcpy(vga, vga + 80, 24 * 80 * 2);
    for (int i = 24 * 80; i < 25 * 80; i++) {
        vga[i] = (0x07 << 8) | ' ';
    }
    x = 0, y = 24;
    set_cursor(y, x);
}

// 数字转换
void print_number(int num, int base) {
    char buffer[32];
    int i = 30;
    int is_negative = 0;

    if (num == 0) {
        putc('0');
        return;
    }

    if (num < 0 && base == 10) {
        is_negative = 1;
        num = -num;
    }

    buffer[31] = '\0';
    while (num > 0) {
        int digit = num % base;
        buffer[i--] = (digit < 10) ? ('0' + digit) : ('a' + digit - 10);
        num /= base;
    }

    if (is_negative) {
        buffer[i--] = '-';
    }

    puts(&buffer[i + 1]);
}

void print_unsigned(u32 value) {
    char buf[11];  // 最大 4294967295 + '\0'
    int i = 0;

    if (value == 0) {
        putc('0');
        return;
    }

    while (value > 0) {
        buf[i++] = '0' + (value % 10);
        value /= 10;
    }

    // 倒序输出
    while (i--) {
        putc(buf[i]);
    }
}


void vprintk(const char *format, va_list args) {
    while (*format) {
        if (*format == '%') {
            format++;

            // 支持 %.ns 形式
            int precision = -1;
            if (*format == '.') {
                format++;
                precision = 0;
                while (*format >= '0' && *format <= '9') {
                    precision = precision * 10 + (*format - '0');
                    format++;
                }
            }

            switch (*format) {
                case 'd':
                    print_number(va_arg(args, int), 10);
                    break;
                case 'u':
                    print_unsigned(va_arg(args, u32));
                    break;
                case 'x': {
                    puts("0x");
                    char buf[32];
                    itoa_hex(va_arg(args, u32), buf);
                    puts(buf);
                    break;
                }
                case 'c':
                    putc((char)va_arg(args, int));
                    break;
                case 's': {
                    char *str = va_arg(args, char *);
                    if (precision >= 0) {
                        for (int i = 0; i < precision && str[i]; i++) {
                            putc(str[i]);
                        }
                    } else {
                        puts(str);
                    }
                    break;
                }
                case 'p': {
                    u32 ptr = va_arg(args, u32);
                    char buf[32];
                    itoa_hex(ptr, buf);
                    puts("0x");
                    puts(buf);
                    break;
                }
                case 'l': {
                    format++;
                    if (*format == 'l' && *(format + 1) == 'x') {
                        format++;
                        puts("0x");
                        char buf[32];
                        itoa_hex64(va_arg(args, u64), buf);
                        puts(buf);
                    } else if (*format == 'x') {
                        puts("0x");
                        char buf[32];
                        itoa_hex(va_arg(args, unsigned long), buf);
                        puts(buf);
                    } else {
                        putc('%');
                        putc('l');
                        putc(*format);
                    }
                    break;
                }
                default:
                    putc('%');
                    putc(*format);
                    break;
            }
        } else {
            putc(*format);
        }
        format++;
    }
}

void print_time() {
    char *temp = (char *) malloc(32);
    itoa_dec(get_tick_count(), temp);
    puts_color("[TIME ", COLOR_GREEN);
    puts_color(temp, COLOR_GREEN);
    puts_color("] ", COLOR_GREEN);
    free(temp);
}

void printk(const char *format, ...) {
    print_time();
    va_list args;
    va_start(args, format);
    vprintk(format, args);
    va_end(args);
}

void printk_err(const char *format, ...) {
    print_time();
    va_list args;
    va_start(args, format);
    puts_color("Error ", COLOR_RED);
    vprintk(format, args);
    va_end(args);
}
